#include "platform.h"
#include "log.h"
#include <stdio.h>
#include <stdarg.h>
#include <stdlib.h>
#include <string.h>
#include <stdbool.h>
#include <assert.h>
#include <signal.h>
#include "vulkan_global.h"
#include <inttypes.h>
#include <wtypes.h>
#include <vulkan/vulkan_win32.h>
#include <windows.h>


//---------------------------------------------------------------------------------------
//--define
#ifdef _WIN64
#define GWL_USERDATA GWLP_USERDATA
#endif
#define WIDTH 720
#define HEIGHT 720
//---------------------------------------------------------------------------------------
//--static
static HINSTANCE  s_hinstance;
static HWND   s_window;

//---------------------------------------------------------------------------------------
//--global
VkSurfaceKHR g_vksurface=NULL;
VkFormat g_format;
PFN_keyFunc g_keyFunc;
PFN_drawFunc g_drawFunc;
PFN_updateFunc g_updateFunc;
uint32_t g_swapchainImageCount;
VkSwapchainKHR g_swapchain;
SwapchainImageResources* g_swapchain_image_resources;
Image g_depth;
VkRenderPass g_render_pass;
int g_width = WIDTH;
int g_height = HEIGHT;
VkSampleCountFlagBits g_msaaSamples = VK_SAMPLE_COUNT_8_BIT;
Image g_colorImage;

//---------------------------------------------------------------------------------------
//--function
void VKAPI_CALL registerKeyFunc(PFN_keyFunc onKeyPress) {
    g_keyFunc = onKeyPress;
}
void VKAPI_CALL registerDrawFunc(PFN_drawFunc onDraw) {
    g_drawFunc = onDraw;
}
void VKAPI_CALL registerUpdateFunc(PFN_updateFunc onUpdate) {
    g_updateFunc = onUpdate;
}

//  ESWindowProc()
//
//      Main window procedure
//
LRESULT WINAPI ESWindowProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
    LRESULT  lRet = 1;

    switch (uMsg)
    {
    case WM_CREATE:
        break;

    case WM_PAINT:
    {
        if (g_drawFunc) {
            g_drawFunc();
        }
        ValidateRect(s_window, NULL);
    }
    break;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_CHAR:
    case WM_KEYDOWN:
    {
        if (g_keyFunc) {
            g_keyFunc((unsigned char)wParam);
        }
    }
    break;

    default:
        lRet = DefWindowProc(hWnd, uMsg, wParam, lParam);
        break;
    }

    return lRet;
}

static void create_window(void) {
    WNDCLASS wndclass = { 0 };
    DWORD    wStyle = 0;
    RECT     windowRect;
    s_hinstance = GetModuleHandle(NULL);


    wndclass.style = CS_OWNDC;
    wndclass.lpfnWndProc = (WNDPROC)ESWindowProc;
    wndclass.hInstance = s_hinstance;
    wndclass.hbrBackground = (HBRUSH)GetStockObject(BLACK_BRUSH);
    wndclass.lpszClassName = "vulkan1.0";

    if (!RegisterClass(&wndclass))
    {
        return ;
    }

    wStyle = WS_VISIBLE | WS_POPUP | WS_BORDER | WS_SYSMENU | WS_CAPTION;

    // Adjust the window rectangle so that the client area has
    // the correct number of pixels
    windowRect.left = 0;
    windowRect.top = 0;
    windowRect.right = WIDTH;
    windowRect.bottom = HEIGHT;

    AdjustWindowRect(&windowRect, wStyle, FALSE);



    s_window = CreateWindow(
        "vulkan1.0",
        "per_pixel_map",
        wStyle,
        0,
        0,
        windowRect.right - windowRect.left,
        windowRect.bottom - windowRect.top,
        NULL,
        NULL,
        s_hinstance,
        NULL);

   // SetWindowLongPtr(window, GWL_USERDATA, (LONGLONG)(LONG_PTR)Context);


    ShowWindow(s_window, TRUE);
}

VkSurfaceKHR VKAPI_CALL create_surface(void) {
    VkResult  err;
    if (s_window == NULL) {
        create_window();
        }
#if defined(VK_USE_PLATFORM_WIN32_KHR)
        VkWin32SurfaceCreateInfoKHR createInfo;
        createInfo.sType = VK_STRUCTURE_TYPE_WIN32_SURFACE_CREATE_INFO_KHR;
        createInfo.pNext = NULL;
        createInfo.flags = 0;
        createInfo.hinstance = s_hinstance;
        createInfo.hwnd = s_window;
        err = vkCreateWin32SurfaceKHR(g_vkinstance, &createInfo, NULL, &g_vksurface);
#endif

	return g_vksurface;

}
void VKAPI_CALL win_loop(void) {
    MSG msg = { 0 };
    int done = 0;
    DWORD lastTime = GetTickCount();

    while (!done)
    {
        int gotMsg = (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE) != 0);
        DWORD curTime = GetTickCount();
        float deltaTime = (float)(curTime - lastTime) / 1000.0f;
        lastTime = curTime;

        if (gotMsg)
        {
            if (msg.message == WM_QUIT)
            {
                done = 1;
            }
            else
            {
                TranslateMessage(&msg);
                DispatchMessage(&msg);
            }
        }
        else
        {
            SendMessage(s_window, WM_PAINT, 0, 0);
        }

        // Call update function if registered
        if (g_updateFunc != NULL)
        {
            g_updateFunc( deltaTime);
        }
    }
}
static void create_swapchainImages_view(void) {
    VkResult  res;
    res = vkGetSwapchainImagesKHR(g_vkdevice, g_swapchain, &g_swapchainImageCount, NULL);
    assert(res == VK_SUCCESS);

    VkImage* swapchainImages = (VkImage*)malloc(g_swapchainImageCount * sizeof(VkImage));
    assert(swapchainImages);
    res = vkGetSwapchainImagesKHR(g_vkdevice, g_swapchain, &g_swapchainImageCount, swapchainImages);
    assert(res == VK_SUCCESS);

    g_swapchain_image_resources =
        (SwapchainImageResources*)malloc(sizeof(SwapchainImageResources) * g_swapchainImageCount);
    for (uint32_t i = 0; i < g_swapchainImageCount; i++) {

        VkImageViewCreateInfo color_image_view = {
            .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
            .pNext = NULL,
            .format = g_format,
            .components =
                {
                    .r = VK_COMPONENT_SWIZZLE_IDENTITY,
                    .g = VK_COMPONENT_SWIZZLE_IDENTITY,
                    .b = VK_COMPONENT_SWIZZLE_IDENTITY,
                    .a = VK_COMPONENT_SWIZZLE_IDENTITY,
                },
            .subresourceRange =
                {.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1},
            .viewType = VK_IMAGE_VIEW_TYPE_2D,
            .flags = 0,
        };

        g_swapchain_image_resources[i].image = swapchainImages[i];

        color_image_view.image = g_swapchain_image_resources[i].image;

        res = vkCreateImageView(g_vkdevice, &color_image_view, NULL, &g_swapchain_image_resources[i].view);
        assert(!res);
    }
    if (NULL != swapchainImages) {
        free(swapchainImages);
    }
}
 bool memory_type_from_properties( uint32_t typeBits, VkFlags requirements_mask, uint32_t* typeIndex) {
    // Search memtypes to find first index with those properties
    for (uint32_t i = 0; i < VK_MAX_MEMORY_TYPES; i++) {
        if ((typeBits & 1) == 1) {
            // Type is available, does it match user properties?
            if ((memory_properties.memoryTypes[i].propertyFlags & requirements_mask) == requirements_mask) {
                *typeIndex = i;
                return true;
            }
        }
        typeBits >>= 1;
    }
    // No memory types matched, return failure
    return false;
}
 //VkImage textureImage;
//VkDeviceMemory textureImageMemory;
 uint32_t findMemoryType(uint32_t typeFilter, VkMemoryPropertyFlags properties) {
     VkPhysicalDeviceMemoryProperties memProperties;
     vkGetPhysicalDeviceMemoryProperties(g_physical_devices, &memProperties);

     for (uint32_t i = 0; i < memProperties.memoryTypeCount; i++) {
         if ((typeFilter & (1 << i)) && (memProperties.memoryTypes[i].propertyFlags & properties) == properties) {
             return i;
         }
     }
     return 0;
 }

 void createImage(uint32_t width, uint32_t height, VkSampleCountFlagBits numSamples, VkFormat format, VkImageTiling tiling, VkImageUsageFlags usage, VkMemoryPropertyFlags properties, VkImage* image, VkDeviceMemory* imageMemory)
 {
     VkImageCreateInfo imageInfo = {
         .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
         .imageType = VK_IMAGE_TYPE_2D,
         .extent.width = width,
         .extent.height = height,
         .extent.depth = 1,
         .mipLevels = 1,
         .arrayLayers = 1,
         .format = format ,
         .tiling = tiling ,
         .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
         .usage = usage,
         .samples = numSamples,
         .sharingMode = VK_SHARING_MODE_EXCLUSIVE,
         .pNext = NULL,
         .flags = 0,
     };

     if (vkCreateImage(g_vkdevice, &imageInfo, NULL, image) != VK_SUCCESS) {
         log_e("failed to create image");
         return;
     }

     VkMemoryRequirements memRequirements;
     vkGetImageMemoryRequirements(g_vkdevice, *image, &memRequirements);

     VkMemoryAllocateInfo allocInfo = {
     .sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
     .allocationSize = memRequirements.size,
     .memoryTypeIndex = findMemoryType(memRequirements.memoryTypeBits, properties),
     };

     if (vkAllocateMemory(g_vkdevice, &allocInfo, NULL, imageMemory) != VK_SUCCESS) {
         log_e("failed to allocate image memory");
         return;
     }

     vkBindImageMemory(g_vkdevice, *image, *imageMemory, 0);
 }

 VkImageView createImageView(VkImage image, VkFormat format, VkImageAspectFlags aspectFlags, uint32_t mipLevels) {
     VkImageViewCreateInfo viewInfo;
     memset(&viewInfo, 0, sizeof(viewInfo));
     viewInfo.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;
     viewInfo.image = image;
     viewInfo.viewType = VK_IMAGE_VIEW_TYPE_2D;
     viewInfo.format = format;
     viewInfo.subresourceRange.aspectMask = aspectFlags;
     viewInfo.subresourceRange.baseMipLevel = 0;
     viewInfo.subresourceRange.levelCount = mipLevels;
     viewInfo.subresourceRange.baseArrayLayer = 0;
     viewInfo.subresourceRange.layerCount = 1;

     VkImageView imageView;
     if (vkCreateImageView(g_vkdevice, &viewInfo, NULL, &imageView) != VK_SUCCESS) {
         log_e("failed to create texture image view!");
     }

     return imageView;
 }

 void create_color_image_view() {
     VkFormat colorFormat = g_format;

     createImage(WIDTH, HEIGHT,  g_msaaSamples, colorFormat, VK_IMAGE_TILING_OPTIMAL, VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT | VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, &g_colorImage.image, &g_colorImage.mem);
     g_colorImage.view = createImageView(g_colorImage.image, colorFormat, VK_IMAGE_ASPECT_COLOR_BIT, 1);

}

void create_depth_image_view(void) {
    const VkFormat depth_format = VK_FORMAT_D16_UNORM;
    const VkImageCreateInfo image = {
    .sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
    .pNext = NULL,
    .imageType = VK_IMAGE_TYPE_2D,
    .format = depth_format,
    .extent = {WIDTH, HEIGHT, 1},
    .mipLevels = 1,
    .arrayLayers = 1,
    .samples = g_msaaSamples,
    .tiling = VK_IMAGE_TILING_OPTIMAL,
    .usage = VK_IMAGE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT,
    .flags = 0,
    };

    VkImageViewCreateInfo view = {
        .sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO,
        .pNext = NULL,
        .image = VK_NULL_HANDLE,
        .format = depth_format,
        .subresourceRange =
            {.aspectMask = VK_IMAGE_ASPECT_DEPTH_BIT, .baseMipLevel = 0, .levelCount = 1, .baseArrayLayer = 0, .layerCount = 1},
        .flags = 0,
        .viewType = VK_IMAGE_VIEW_TYPE_2D,
    };

    //if (demo->force_errors) {
    //    // Intentionally force a bad pNext value to generate a validation layer error
    //    view.pNext = &image;
    //}

    VkMemoryRequirements mem_reqs;
    VkResult  err;
    bool  pass;

    g_depth.format = depth_format;

    /* create image */
    err = vkCreateImage(g_vkdevice, &image, NULL, &g_depth.image);
    assert(!err);

    vkGetImageMemoryRequirements(g_vkdevice, g_depth.image, &mem_reqs);
    assert(!err);

    g_depth.mem_alloc.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;
    g_depth.mem_alloc.pNext = NULL;
    g_depth.mem_alloc.allocationSize = mem_reqs.size;
    g_depth.mem_alloc.memoryTypeIndex = 0;

    pass = memory_type_from_properties( mem_reqs.memoryTypeBits, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,
        &g_depth.mem_alloc.memoryTypeIndex);
    assert(pass);

    /* allocate memory */
    err = vkAllocateMemory(g_vkdevice, &g_depth.mem_alloc, NULL, &g_depth.mem);
    assert(!err);

    /* bind memory */
    err = vkBindImageMemory(g_vkdevice, g_depth.image, g_depth.mem, 0);
    assert(!err);

    /* create image view */
    view.image = g_depth.image;
    err = vkCreateImageView(g_vkdevice, &view, NULL, &g_depth.view);
    assert(!err);
}

void VKAPI_CALL init_swapchain(void) {
    // Get the list of VkFormats that are supported:
    uint32_t formatCount;
    VkResult  res;


    res = vkGetPhysicalDeviceSurfaceFormatsKHR(g_physical_devices, g_vksurface, &formatCount, NULL);
    assert(res == VK_SUCCESS);
    VkSurfaceFormatKHR* surfFormats = (VkSurfaceFormatKHR*)malloc(formatCount * sizeof(VkSurfaceFormatKHR));
    res = vkGetPhysicalDeviceSurfaceFormatsKHR(g_physical_devices, g_vksurface, &formatCount, surfFormats);
    assert(res == VK_SUCCESS);

    // If the device supports our preferred surface format, use it.
    // Otherwise, use whatever the device's first reported surface
    // format is.
    assert(formatCount >= 1);
    g_format = surfFormats[0].format;
    for (size_t i = 0; i < formatCount; ++i) {
        if (surfFormats[i].format == VK_FORMAT_B8G8R8A8_UNORM) {
            g_format = VK_FORMAT_B8G8R8A8_UNORM;
            break;
        }
    }


    VkSurfaceCapabilitiesKHR surfCapabilities;

    res = vkGetPhysicalDeviceSurfaceCapabilitiesKHR(g_physical_devices, g_vksurface, &surfCapabilities);
    assert(res == VK_SUCCESS);

    uint32_t presentModeCount;
    res = vkGetPhysicalDeviceSurfacePresentModesKHR(g_physical_devices, g_vksurface, &presentModeCount, NULL);
    assert(res == VK_SUCCESS);
    VkPresentModeKHR* presentModes = (VkPresentModeKHR*)malloc(presentModeCount * sizeof(VkPresentModeKHR));
    assert(presentModes);
    res = vkGetPhysicalDeviceSurfacePresentModesKHR(g_physical_devices, g_vksurface, &presentModeCount, presentModes);
    assert(res == VK_SUCCESS);
     

    VkExtent2D swapchainExtent;
    if (surfCapabilities.currentExtent.width == 0xFFFFFFFF) {
        // If the surface size is undefined, the size is set to
        // the size of the images requested.
        swapchainExtent.width = WIDTH;
        swapchainExtent.height = HEIGHT;
        if (swapchainExtent.width < surfCapabilities.minImageExtent.width) {
            swapchainExtent.width = surfCapabilities.minImageExtent.width;
        }
        else if (swapchainExtent.width > surfCapabilities.maxImageExtent.width) {
            swapchainExtent.width = surfCapabilities.maxImageExtent.width;
        }

        if (swapchainExtent.height < surfCapabilities.minImageExtent.height) {
            swapchainExtent.height = surfCapabilities.minImageExtent.height;
        }
        else if (swapchainExtent.height > surfCapabilities.maxImageExtent.height) {
            swapchainExtent.height = surfCapabilities.maxImageExtent.height;
        }
    }
    else {
        // If the surface size is defined, the swap chain size must match
        swapchainExtent = surfCapabilities.currentExtent;
    }


    // Determine the number of VkImage's to use in the swap chain.
// We need to acquire only 1 presentable image at at time.
// Asking for minImageCount images ensures that we can acquire
// 1 presentable image as long as we present it before attempting
// to acquire another.
    uint32_t desiredNumberOfSwapChainImages = surfCapabilities.minImageCount;

    VkSurfaceTransformFlagBitsKHR preTransform;
    if (surfCapabilities.supportedTransforms & VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR) {
        preTransform = VK_SURFACE_TRANSFORM_IDENTITY_BIT_KHR;
    }
    else {
        preTransform = surfCapabilities.currentTransform;
    }

    // Find a supported composite alpha mode - one of these is guaranteed to be set
    VkCompositeAlphaFlagBitsKHR compositeAlpha = VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR;
    VkCompositeAlphaFlagBitsKHR compositeAlphaFlags[4] = {
        VK_COMPOSITE_ALPHA_OPAQUE_BIT_KHR,
        VK_COMPOSITE_ALPHA_PRE_MULTIPLIED_BIT_KHR,
        VK_COMPOSITE_ALPHA_POST_MULTIPLIED_BIT_KHR,
        VK_COMPOSITE_ALPHA_INHERIT_BIT_KHR,
    };
    for (uint32_t i = 0; i < sizeof(compositeAlphaFlags) / sizeof(compositeAlphaFlags[0]); i++) {
        if (surfCapabilities.supportedCompositeAlpha & compositeAlphaFlags[i]) {
            compositeAlpha = compositeAlphaFlags[i];
            break;
        }
    }
    // The FIFO present mode is guaranteed by the spec to be supported
// Also note that current Android driver only supports FIFO
    VkPresentModeKHR swapchainPresentMode = VK_PRESENT_MODE_FIFO_KHR;
    bool bFound = false;
    for (size_t i = 0; i < presentModeCount; ++i) {
        if (presentModes[i] == swapchainPresentMode) {
            bFound = true;
            break;
        }
    }
    assert(bFound);

    VkSwapchainCreateInfoKHR swapchain_ci = {
    .sType = VK_STRUCTURE_TYPE_SWAPCHAIN_CREATE_INFO_KHR,
    .pNext = NULL,
    .surface = g_vksurface,
    .minImageCount = desiredNumberOfSwapChainImages,
    .imageFormat = g_format,
    .imageExtent.width = swapchainExtent.width,
    .imageExtent.height = swapchainExtent.height,
    .preTransform = preTransform,
    .compositeAlpha = compositeAlpha,
    .imageArrayLayers = 1,
    .presentMode = swapchainPresentMode,
    .oldSwapchain = VK_NULL_HANDLE,
    .clipped = true,
    .imageColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR,
    .imageUsage = VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT,
    .imageSharingMode = VK_SHARING_MODE_EXCLUSIVE,
    .queueFamilyIndexCount = 0,
    .pQueueFamilyIndices = NULL,
    };
    uint32_t queueFamilyIndices[2] = { (uint32_t)graphicsQueueFamilyIndex, (uint32_t)presentQueueFamilyIndex };
    if (graphicsQueueFamilyIndex != presentQueueFamilyIndex) {
        // If the graphics and present queues are from different queue families,
        // we either have to explicitly transfer ownership of images between the
        // queues, or we have to create the swapchain with imageSharingMode
        // as VK_SHARING_MODE_CONCURRENT
        swapchain_ci.imageSharingMode = VK_SHARING_MODE_CONCURRENT;
        swapchain_ci.queueFamilyIndexCount = 2;
        swapchain_ci.pQueueFamilyIndices = queueFamilyIndices;
    }
    res = vkCreateSwapchainKHR(g_vkdevice, &swapchain_ci, NULL, &g_swapchain);
    assert(res == VK_SUCCESS);



    if (NULL != presentModes) {
        free(presentModes);
    }
    create_color_image_view();

    create_swapchainImages_view();
    create_depth_image_view();
    init_render_pass();


}
void VKAPI_CALL init_render_pass(void) {
   const VkAttachmentDescription attachments[3] = {
        [0] =
        {
            .format = g_format,
            .flags = 0,
            .samples = g_msaaSamples,
            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            //.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
            .finalLayout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
        },
        [1] =
        {
            .format = g_depth.format,
            .flags = 0,
            .samples = g_msaaSamples,
            .loadOp = VK_ATTACHMENT_LOAD_OP_CLEAR,
            .storeOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            .finalLayout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
        },
        [2] =
        {
            .format = g_format,
            .flags = 0,
            .samples = VK_SAMPLE_COUNT_1_BIT,
            .loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .storeOp = VK_ATTACHMENT_STORE_OP_STORE,
            .stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE,
            .stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE,
            .initialLayout = VK_IMAGE_LAYOUT_UNDEFINED,
            .finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
        },

    };

    const VkAttachmentReference color_reference = {
        .attachment = 0,
        .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    };
    const VkAttachmentReference depth_reference = {
        .attachment = 1,
        .layout = VK_IMAGE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL,
    };
    const VkAttachmentReference resolve_reference = {
    .attachment = 2,
    .layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL,
    };

    const VkSubpassDescription subpass = {
        .pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS,
        .flags = 0,
        .inputAttachmentCount = 0,
        .pInputAttachments = NULL,
        .colorAttachmentCount = 1,
        .pColorAttachments = &color_reference,
        .pResolveAttachments = &resolve_reference,
        .pDepthStencilAttachment = &depth_reference,
        .preserveAttachmentCount = 0,
        .pPreserveAttachments = NULL,
    };


    VkSubpassDependency attachmentDependencies[2] = {
        [0] =
            {
            // Depth buffer is shared between swapchain images
            .srcSubpass = VK_SUBPASS_EXTERNAL,
            .dstSubpass = 0,
            .srcStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | VK_PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT,
            .srcAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
            .dstAccessMask = VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | VK_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT,
            .dependencyFlags = 0,
        },
        [1] =
        {
            // Image Layout Transition
            .srcSubpass = VK_SUBPASS_EXTERNAL,
            .dstSubpass = 0,
            .srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT,
            .srcAccessMask = 0,
            .dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT,
            .dependencyFlags = 0,
        },
    };

    const VkRenderPassCreateInfo rp_info = {
        .sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO,
        .pNext = NULL,
        .flags = 0,
        .attachmentCount = 3,
        .pAttachments = attachments,
        .subpassCount = 1,
        .pSubpasses = &subpass,
        .dependencyCount = 2,
        .pDependencies = attachmentDependencies,
    };
    VkResult  err;

    err = vkCreateRenderPass(g_vkdevice, &rp_info, NULL, &g_render_pass);
    assert(!err);

    init_framebuffer();
}

void VKAPI_CALL init_framebuffer(void) {
    VkImageView attachments[3];
    attachments[0] = g_colorImage.view;
    attachments[1] = g_depth.view;

    const VkFramebufferCreateInfo fb_info = {
        .sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO,
        .pNext = NULL,
        .renderPass = g_render_pass,
        .attachmentCount = 3,
        .pAttachments = attachments,
        .width = WIDTH,
        .height = HEIGHT,
        .layers = 1,
    };
    VkResult  err;
    uint32_t i;

    for (i = 0; i < g_swapchainImageCount; i++) {
        attachments[2] = g_swapchain_image_resources[i].view;
        err = vkCreateFramebuffer(g_vkdevice, &fb_info, NULL, &g_swapchain_image_resources[i].framebuffer);
        assert(!err);
    }
}
